package main

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/golang-module/carbon"
	"github.com/robfig/cron"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

// 与填写的服务器配置中的Token一致
const Token = "ZZTMSz"

// WXTextMsg 微信文本消息结构体
type WXTextMsg struct {
	ToUserName   string
	FromUserName string
	CreateTime   int64
	MsgType      string
	Content      string
	MsgId        int64
}

var AccessToken string

func main() {
	go func() {
		tick := time.NewTicker(time.Hour)
		defer tick.Stop()
		for _ = range tick.C {
			getToken()
		}
	}()
	go func() {
		spec := "0 30 6 * * ?"
		c := cron.New()
		c.AddFunc(spec, func() {
			sendTemplateMessage()
		})
		c.Start()
	}()
	router := gin.Default()

	router.GET("/wx", WXCheckSignature)
	router.POST("/wx", WXMsgReceive)

	log.Fatalln(router.Run(":9291"))
}

type V struct {
	Date struct {
		Value string `json:"value"`
		Color string `json:"color"`
	} `json:"date"`
	Time struct {
		Value string `json:"value"`
		Color string `json:"color"`
	} `json:"time"`
	See struct {
		Value string `json:"value"`
		Color string `json:"color"`
	} `json:"see"`
	Love struct {
		Value string `json:"value"`
		Color string `json:"color"`
	} `json:"love"`
	Birthday struct {
		Value string `json:"value"`
		Color string `json:"color"`
	} `json:"birthday"`
}

type Template struct {
	Touser     string `json:"touser"`
	TemplateId string `json:"template_id"`
	Data       struct {
		V
	} `json:"data"`
}

func sendTemplateMessage() {
	// ZivGDLyEet4LJqzWvqrJoTysNNlDVzDaoe_16d8wRqQ
	url := "https://api.weixin.qq.com/cgi-bin/message/template/send?" +
		"access_token=" + AccessToken
	method := "POST"
	v := V{
		Date: struct {
			Value string `json:"value"`
			Color string `json:"color"`
		}{
			Value: carbon.Now().ToDateString(),
			Color: "#ff4777",
		},
		Time: struct {
			Value string `json:"value"`
			Color string `json:"color"`
		}{
			Value: carbon.Now().ToTimeString(),
			Color: "#ff4777",
		},
		See: struct {
			Value string `json:"value"`
			Color string `json:"color"`
		}{
			Value: fmt.Sprintf("%d", carbon.Parse("2021-11-16").DiffInDays(carbon.Now())),
			Color: "#ff4777",
		},
		Love: struct {
			Value string `json:"value"`
			Color string `json:"color"`
		}{
			Value: fmt.Sprintf("%d", carbon.Parse("2021-12-24").DiffInDays(carbon.Now())),
			Color: "#ff4777",
		},
		Birthday: struct {
			Value string `json:"value"`
			Color string `json:"color"`
		}{
			Value: fmt.Sprintf("%d", carbon.Parse(carbon.Now().ToDateString()).DiffInDays(carbon.Parse("2023-03-31"))),
			Color: "#ff4777",
		},
	}
	users := []string{"oIO6q5ytnbfnClHKk2pXxkcLgH8Y", "oIO6q5yLuaip6GA8hZH9cyzc1ctw"}
	for _, user := range users {
		template := Template{
			Touser:     user,
			TemplateId: "DowI7RTmrquSIDW2OR8YMxX3pfJBPrKYqLGkez223m8",
			Data:       struct{ V }{V: v},
		}
		bytes, err := json.Marshal(template)
		if err != nil {
			return
		}
		payload := strings.NewReader(string(bytes))

		client := &http.Client{}
		req, err := http.NewRequest(method, url, payload)

		if err != nil {
			fmt.Println(err)
			return
		}
		req.Header.Add("Content-Type", "application/json")

		res, err := client.Do(req)
		if err != nil {
			fmt.Println(err)
			return
		}
		defer res.Body.Close()

		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			fmt.Println(err)
			return
		}
		fmt.Println(string(body))
	}

}

type TokenResp struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int    `json:"expires_in"`
}

func getToken() {
	req, err := http.NewRequest("GET", "https://api.weixin.qq.com/cgi-bin/token", nil)
	if err != nil {
		log.Print(err)
		os.Exit(1)
	}

	q := req.URL.Query()
	q.Add("grant_type", "client_credential")
	q.Add("appid", "wx519266c627a88100")
	q.Add("secret", "4f0598e6393ba737b99f0e630e98523e")
	req.URL.RawQuery = q.Encode()

	fmt.Println(req.URL.String())
	// Output:
	// http://api.themoviedb.org/3/tv/popular?another_thing=foo+%26+bar&api_key=key_from_environment_or_flag
	var resp *http.Response
	resp, err = http.DefaultClient.Do(req)
	if err != nil {
		log.Print(err)
	}
	body, err := ioutil.ReadAll(resp.Body)
	fmt.Println(string(body))
	m := make(map[string]string)
	err = json.Unmarshal(body, &m)
	if err != nil {
		fmt.Println(err)
	}
	ok := false
	if AccessToken, ok = m["access_token"]; !ok {
		fmt.Println(m["errcode"])
		fmt.Println(m["errmsg"])
	}
	fmt.Println(AccessToken)
	defer resp.Body.Close()
}

// WXCheckSignature 微信接入校验
func WXCheckSignature(c *gin.Context) {
	signature := c.Query("signature")
	timestamp := c.Query("timestamp")
	nonce := c.Query("nonce")
	echostr := c.Query("echostr")

	ok := CheckSignature(signature, timestamp, nonce, Token)
	if !ok {
		log.Println("微信公众号接入校验失败!")
		return
	}

	log.Println("微信公众号接入校验成功!")
	_, _ = c.Writer.WriteString(echostr)
}

// WXRepTextMsg 微信回复文本消息结构体
type WXRepTextMsg struct {
	ToUserName   string
	FromUserName string
	CreateTime   int64
	MsgType      string
	Content      string
	// 若不标记XMLName, 则解析后的xml名为该结构体的名称
	XMLName xml.Name `xml:"xml"`
}

// WXMsgReceive 微信消息接收
func WXMsgReceive(c *gin.Context) {
	var textMsg WXTextMsg
	err := c.ShouldBindXML(&textMsg)
	if err != nil {
		log.Printf("[消息接收] - XML数据包解析失败: %v\n", err)
		return
	}
	log.Printf("%#v", textMsg)

	log.Printf("[消息接收] - 收到消息, 消息类型为: %s, 消息内容为: %s\n", textMsg.MsgType, textMsg.Content)
	// 对接收的消息进行被动回复
	WXMsgReply(c, textMsg.ToUserName, textMsg.FromUserName)
}

// WXMsgReply 微信消息回复
func WXMsgReply(c *gin.Context, fromUser, toUser string) {
	repTextMsg := WXRepTextMsg{
		ToUserName:   toUser,
		FromUserName: fromUser,
		CreateTime:   time.Now().Unix(),
		MsgType:      "text",
		Content:      fmt.Sprintf("[消息回复] - %s", time.Now().Format("2006-01-02 15:04:05")),
	}

	msg, err := xml.Marshal(&repTextMsg)
	if err != nil {
		log.Printf("[消息回复] - 将对象进行XML编码出错: %v\n", err)
		return
	}
	_, _ = c.Writer.Write(msg)
}
